package model;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;

import database.Clause;
import database.ValuedAttribute;

import utils.StrUtils;

/**
 * Classe de communication avec la table 'room' de la BDD
 * @author Aubry Pirian
 * version 1.0
 *
 */
public class Room extends DbTable {
	
	private static final String DEFAULT_ADDRESS = "localhost";	// Adresse par défaut
	
	private int id=-1;		// Numéro de la salle
	private String name;	// Nom de la salle
	private String address;	// Adresse
	private String status;	// Statut (open, hidden, closed)
	
	private boolean linked = false;	// si la table est liée à une entrée
	
	/**
	 * Constructeur de nouvelle Salle
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 * @throws ClassNotFoundException : impossible d'accéder au driver
	 */
	public Room () throws ClassNotFoundException, SQLException, NoSuchFieldException {
		super(Params.ROOM_TABLE);
	}
	
	/**
	 * Constructeur de salle par numéro
	 * @param id : numéro de la salle
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 * @throws ClassNotFoundException : impossible d'accéder au driver
	 */
	public Room (int id) throws ClassNotFoundException, SQLException, NoSuchFieldException {
		super(Params.ROOM_TABLE);
		this.id=id;
		try {
			this.read(id);
			this.linked=true;
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
	}
	
	/**
	 * Constructeur de salle par nom
	 * @param name : nom de la salle
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 * @throws ClassNotFoundException : impossible d'accéder au driver
	 */
	public Room(String name) throws ClassNotFoundException, SQLException, NoSuchFieldException {
		super(Params.ROOM_TABLE);
		try {
			this.read(name);
			this.linked=true;
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
	}
	
	/**
	 * Méthode récupérant les informations dans la BDD, par numéro
	 * @param id : numéro de la salle
	 * @throws SQLException 
	 * @throws NoSuchFieldException : paramètre non valide
	 */
	public void read (int id) throws SQLException, NoSuchFieldException {
		ResultSet rs;
		try {
			rs = this.idbc.select(this.tableName,this.getWhere("id"));
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			if (rs.next()) {
				try {
					//this.id = rs.getInt("id");
					this.name = rs.getString("name");
					this.address = rs.getString("address");
					this.status = rs.getString("status");
				} catch (SQLException e) {
					throw e;
				}
			} else {
				throw new SQLException("Salle inconnue.","Unknown room");
			}
		} catch (SQLException e) {
			throw e;
		}
	}
	
	/**
	 * Méthode récupérant les informations dans la BDD, par nom
	 * @param name : nom de la salle
	 * @throws SQLException 
	 * @throws NoSuchFieldException : paramètre invalide
	 */
	public void read (String name) throws SQLException, NoSuchFieldException {
		this.name = name;
		ResultSet rs;
		try {
			rs = this.idbc.select(this.tableName,this.getWhere("name"));
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			if (rs.next()) {
				try {
					this.id = rs.getInt("id");
					//this.name = rs.getString("name");
					this.address = rs.getString("address");
					this.status = rs.getString("status");
				} catch (SQLException e) {
					throw e;
				}
			} else {
				throw new SQLException("Salle inconnue.","Unknown room");
			}
		} catch (SQLException e) {
			throw e;
		}
	}
	
	/**
	 * Méthode enregistrant la salle dans la table de salles
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 */
	public void save() throws NoSuchFieldException, SQLException {
		if (!this.linked) {
			try {
				this.create(this.name, this.address);
			} catch (NoSuchFieldException e) {
				throw e;
			} catch (SQLException e) {
				throw e;
			}
		} else {
			this.setId(this.id);
			this.setAddress(this.address);
			this.setName(this.name);
			this.setStatus(this.status);
		}
	}
	
	/**
	 * Méthode ajoutant la salle dans la table de salles, avec l'adresse par défaut
	 * @param name : nom de la salle
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 */
	public void create(String name) throws NoSuchFieldException, SQLException {
		try {
			this.create(name, null);
			this.linked=true;
		} catch (NoSuchFieldException e) {
			throw e;
		} catch (SQLException e) {
			throw e;
		}
	}
	
	/**
	 * Méthode ajoutant la salle dans la table de salles
	 * @param name		: nom de la salle
	 * @param address	: adresse de la salle
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 */
	public void create(String name, String address) throws NoSuchFieldException, SQLException {
		LinkedList<ValuedAttribute> values = new LinkedList<ValuedAttribute>();
		if (name==null || name.length()==0)
			throw new NoSuchFieldException("Nom de salle invalide.");
		if (!StrUtils.isNodeName(name))
				throw new NoSuchFieldException("Nom de salle invalide.");
		try {
			values.add( new ValuedAttribute("name",name) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			if (address==null) values.add( new ValuedAttribute("address",DEFAULT_ADDRESS) );
			else values.add( new ValuedAttribute("address",address) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			this.idbc.insert(this.tableName, values);
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		this.linked=true;
		this.read(name);
	}
	
	/**
	 * Méthode supprimant la salle de la table de salles
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 */
	public void delete() throws NoSuchFieldException, SQLException {
		try {
			this.idbc.delete(this.tableName, this.getWhere("id"));
			this.linked=false;
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
	}
	
	/**
	 * Accesseur du numéro de la salle
	 * @return numéro de la salle
	 */
	public int getId() { return this.id; }
	/**
	 * Accesseur du nom de la salle
	 * @return nom de la salle
	 */
	public String getName() { return this.name; }
	/**
	 * Accesseur de l'adresse de la salle
	 * @return adresse de la salle
	 */
	public String getAddress() { return this.address; }
	/**
	 * Accesseur du statut de la salle
	 * @return statut de la salle
	 */
	public String getStatus() { return this.status; }
	
	/**
	 * Méthode retournant la clause identifiant la salle dans la table,
	 * 	en fonction du numéro
	 * @return Clause where
	 * @throws NoSuchFieldException : paramètre non valide
	 */
	private Clause getWhere(String key) throws NoSuchFieldException {
		if (key.equals("id")) {
			try {
				return new Clause( new ValuedAttribute("id",""+this.id) );
			} catch (NoSuchFieldException e) {
				throw e;
			}
		} else if (key.equals("name")) {
			try {
				return new Clause( new ValuedAttribute("name",""+this.name) );
			} catch (NoSuchFieldException e) {
				throw e;
			}
		}
		return null;
	}
	
	/**
	 * Modifieur du numéro de la salle
	 * @param id : nouveau numéro
	 * @throws SQLException 
	 * @throws NoSuchFieldException : paramètre non valide
	 */
	public void setId (int id) throws SQLException, NoSuchFieldException {
		if (!linked) this.save();
		LinkedList<ValuedAttribute> values = new LinkedList<ValuedAttribute>();
		try {
			values.add( new ValuedAttribute("id",""+id) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			this.idbc.update(this.tableName, values, this.getWhere("id"));
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		this.id=id;
	}
	
	/**
	 * Modifieur du nom de la salle
	 * @param name : nouveau nom
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 */
	public void setName (String name) throws NoSuchFieldException, SQLException {
		if (!linked) this.save();
		LinkedList<ValuedAttribute> values = new LinkedList<ValuedAttribute>();
		try {
			values.add( new ValuedAttribute("name",name) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			this.idbc.update(this.tableName, values, this.getWhere("id"));
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		this.name=name;
	}
	
	/**
	 * Modifieur de l'adresse de la salle
	 * @param id : nouvelle adresse
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 */
	public void setAddress (String address) throws NoSuchFieldException, SQLException {
		if (!linked) this.save();
		LinkedList<ValuedAttribute> values = new LinkedList<ValuedAttribute>();
		try {
			values.add( new ValuedAttribute("address",address) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			this.idbc.update(this.tableName, values, this.getWhere("id"));
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		this.address=address;
	}
	
	/**
	 * Modifieur du statut de la salle
	 * @param name : nouveau statut
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 */
	public void setStatus (String status) throws NoSuchFieldException, SQLException {
		if (!linked) this.save();
		LinkedList<ValuedAttribute> values = new LinkedList<ValuedAttribute>();
		try {
			values.add( new ValuedAttribute("status",status) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		try {
			this.idbc.update(this.tableName, values, this.getWhere("id"));
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		this.status=status;
	}
	
	/**
	 * Méthode retournant le nombre de connexions à la salle d'un membre, à une date donnée
	 * @param member	: numéro du membre
	 * @param beg	: date minimum ( 'day'->jour ; 'month'->mois; 'year'->année )
	 * @param end	: date maximum ( 'day'->jour ; 'month'->mois; 'year'->année )
	 * @return nombre de connexions
	 * @throws SQLException 
	 * @throws ClassNotFoundException : impossible d'accéder au driver
	 * @throws NoSuchFieldException : paramètre non valide
	 */
	public int getConnections (int member, HashMap<String,Integer> beg, HashMap<String,Integer> end) throws SQLException, ClassNotFoundException, NoSuchFieldException {
		if (member>=0) {
			Member m;
			try {
				m = new Member(member);
			} catch (ClassNotFoundException e) {
				throw e;
			} catch (SQLException e) {
				throw e;
			} catch (NoSuchFieldException e) {
				throw e;
			}
			try {
				return m.getConnections(this.id, beg, end);
			} catch (NoSuchFieldException e) {
				throw e;
			} catch (SQLException e) {
				throw e;
			}
		} else {
			try {
				return this.getConnections(beg, end);
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			}
		}
		return -1;
	}
	
	/**
	 * Méthode retournant le nombre de connexions à la salle d'un membre, à une date donnée
	 * @param beg	: date minimum ( 'day'->jour ; 'month'->mois; 'year'->année )
	 * @param end	: date maximum ( 'day'->jour ; 'month'->mois; 'year'->année )
	 * @return nombre de connexions
	 * @throws SQLException 
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws Exception
	 */
	public int getConnections (HashMap<String,Integer> beg, HashMap<String,Integer> end) throws SQLException, NoSuchFieldException {
		Clause where;
		try {
			where = new Clause( new ValuedAttribute("room",this.id) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		if (beg!=null) {
			String d = "";
			int m;
			if (beg.get("year")!=null) {
				d += beg.get("year");
				if (beg.get("month")!=null) {
					m = beg.get("month");
					d += "-";
					if (m<10) d += "0";
					d += beg.get("month");
					if (beg.get("day")!=null) {
						m = beg.get("day");
						d += "-";
						if (m<10) d += "0";
						d += beg.get("day");
					}// else d+="-00";
				}// else d+="-00-00";
				Clause dc;
				try {
					dc = new Clause( new ValuedAttribute("date_out",d,">") );
				} catch (NoSuchFieldException e) {
					throw e;
				}
				try {
					where = new Clause ("AND", where, dc);
				} catch (NoSuchFieldException e) {
					throw e;
				}
			}
		}
		if (end!=null) {
			String d = "";
			int m;
			if (end.get("year")!=null) {
				d += end.get("year");
				if (end.get("month")!=null) {
					m = end.get("month");
					d += "-";
					if (m<10) d += "0";
					d += end.get("month");
					if (end.get("day")!=null) {
						m = end.get("day");
						d += "-";
						if (m<10) d += "0";
						d += end.get("day");
					}// else d+="-00";
				}// else d+="-00-00";
				Clause dc;
				try {
					dc = new Clause( new ValuedAttribute("date_in",d,"<") );
				} catch (NoSuchFieldException e) {
					throw e;
				}
				try {
					where = new Clause ("AND", where, dc);
				} catch (NoSuchFieldException e) {
					throw e;
				}
			}
		}
		ResultSet rs;
		try {
			rs = this.idbc.select(Params.CONNECTION_TABLE,where);
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		int count = 0;
		try {
			while (rs.next()) count+=1;
		} catch (SQLException e) {
			throw e;
		}
		return count;
	}
	
	/**
	 * Méthode retournant le nombre de connexions à la salle d'un membre, à une date donnée
	 * @param member	: numéro du membre
	 * @param date		: date de la connexion
	 * @return nombre de connexions
	 * @throws ClassNotFoundException : impossible d'accéder au driver
	 * @throws SQLException 
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws Exception
	 */
	public int getConnections (int member, HashMap<String,Integer> date) throws ClassNotFoundException, SQLException, NoSuchFieldException {
		if (member>=0) {
				Member m;
				try {
					m = new Member(member);
				} catch (ClassNotFoundException e) {
					throw e;
				} catch (SQLException e) {
					throw e;
				} catch (NoSuchFieldException e) {
					throw e;
				}
				try {
					return m.getConnections(this.id, date);
				} catch (SQLException e) {
					throw e;
				} catch (NoSuchFieldException e) {
					throw e;
				}
		} else {
			try {
				return this.getConnections(date);
			} catch (SQLException e) {
				throw e;
			} catch (NoSuchFieldException e) {
				throw e;
			}
		}
	}
	
	/**
	 * Méthode retournant le nombre de connexions à la salle à une date donnée
	 * @param date		: date de la connexion
	 * @return nombre de connexions
	 * @throws SQLException 
	 * @throws NoSuchFieldException : paramètre non valide
	 */
	public int getConnections (HashMap<String,Integer> date) throws SQLException, NoSuchFieldException {
		Clause where;
		try {
			where = new Clause( new ValuedAttribute("room",this.id) );
		} catch (NoSuchFieldException e) {
			throw e;
		}
		if (date!=null) {
			String d = "";
			if (date.get("year")!=null) d += date.get("year");
			else d += "%";
			d += "-";
			if (date.get("month")!=null) {
				int m = date.get("month");
				if (m<10) d += "0";
				d += date.get("month");
			} else d += "%";
			d += "-";
			if (date.get("day")!=null) {
				int m = date.get("day");
				if (m<10) d += "0";
				d += date.get("day");
			} else d += "%";
			d += " %";
			Clause dc;
			try {
				dc = new Clause( new ValuedAttribute("date_in",d,"like") );
			} catch (NoSuchFieldException e) {
				throw e;
			}
			try {
				where = new Clause ("AND", where, dc);
			} catch (NoSuchFieldException e) {
				throw e;
			}
		}
		ResultSet rs;
		try {
			rs = this.idbc.select(Params.CONNECTION_TABLE,where);
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
		int count = 0;
		try {
			while (rs.next()) {
				count+=1;
			}
		} catch (SQLException e) {
			throw e;
		}
		return count;
	}
	
	/**
	 * Méthode retournant le nombre de connexions à la salle d'un membre
	 * @param member	: numéro du membre
	 * @return nombre de connexions
	 * @throws ClassNotFoundException : impossible d'accéder au driver
	 * @throws SQLException 
	 * @throws NoSuchFieldException : paramètre non valide
	 */
	public int getConnections (int member) throws ClassNotFoundException, SQLException, NoSuchFieldException {
		try {
			return getConnections(member, null);
		} catch (ClassNotFoundException e) {
			throw e;
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
	}

	/**
	 * Méthode retournant le nombre de connexions à la salle
	 * @return nombre de connexions
	 * @throws NoSuchFieldException : paramètre non valide
	 * @throws SQLException 
	 * @throws ClassNotFoundException : impossible d'accéder au driver
	 */
	public int getConnections () throws NoSuchFieldException, SQLException, ClassNotFoundException {
		try {
			return getConnections (-1, null);
		} catch (ClassNotFoundException e) {
			throw e;
		} catch (SQLException e) {
			throw e;
		} catch (NoSuchFieldException e) {
			throw e;
		}
	}

	
	@Override
	public String toString () {
		String s="";
		/*s += "ID : "+this.id+"\n";
		s += "Nom : "+this.name+"\n";
		s += "Adresse : "+this.address+"\n";
		s += "Statut : "+this.status+"\n";*/
		s += "Room :";
		s += this.id+" / ";
		s += this.name+" / ";
		s += this.address+" / ";
		s += this.status;
		return s;
	}


	
	public static void main(String[] args) {
		//FIXME : supprimer ce main
		try {
			Room r;
			// Création d'une salle
			r = new Room();
			r.create("salle1");
			System.out.println(r+"\n\n");
			// Récupération (par nom) de la salle créée
			r = new Room("salle1");
			r.read("salle1");
			System.out.println(r+"\n\n");
			// Suppression d'une salle
			r = new Room("salle1");
			r.delete();
			// Création d'une salle
			r = new Room();
			r.create("salle2");
			System.out.println(r+"\n\n");
			// Récupération de la salle créée
			int id = r.getId();
			r = new Room(id);
			System.out.println(r+"\n\n");
			Stats.main(null);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
